package cpoo.oldNotUsed;


public class Matrix {
	private double[][] matrix;
	private double[][] tempMatrix;
	private int width;
	private int height;
	
	private boolean detCalculated = false;
	private double determinant = 0.0;
	private Matrix inverse = null;

	public Matrix(double[][] matrix) throws MatrixException {
		if (matrix == null || matrix.length == 0) {
			throw new MatrixException(null, "illegal matrix size");
		}
		this.matrix = matrix;
		height = matrix.length;
		width = matrix[0].length;

		if (width == height) {
			tempMatrix = new double[width][height];
		}
	}

	public Matrix(int width, int height) {
		matrix = new double[height][width];
		this.width = width;
		this.height = height;

		if (width == height) {
			tempMatrix = new double[width][height];
		}
	}

	public void setElement(int x, int y, double element) {
		x = x % width;
		y = y % height;

		if (matrix[y][x] != element) {
			matrix[y][x] = element;
			detCalculated = false;
			inverse = null;
		}
	}

	public double getElement(int x, int y) {
		return getElement(matrix, x, y, width, height);
	}

	private double getElement(double[][] actualMatrix, int x, int y,
			int actualWidth, int actualHeight) {
		x = x % actualWidth;
		y = y % actualHeight;

		return actualMatrix[y][x];
	}

	public double getDeterminant() throws MatrixException {
		if (detCalculated){
			return determinant;
		}
		determinant = getDeterminant(matrix, width, height);
		detCalculated = true;
		return determinant;
	}

	private double getDeterminant(double[][] actualMatrix, int actualWidth,
			int actualHeight) throws MatrixException {
		

		if (actualWidth != actualHeight) {
			throw new MatrixException(null, "matrix not square, no determinant");
		}
		
		int[] cols = new int [actualWidth];
		for (int i=0;i<actualWidth;i++) cols[i]=i;
		
		return calculateDeterminant(actualWidth, 0, cols, actualMatrix);
		
	}

	private double calculateDeterminant(int stopien, int wiersz, int wk[], double[][] A) {
		int i, j, k, m;
		int kolumny[] = new int[width]; // wektor kolumn dla podmacierzy
		double suma;

		if (stopien == 1)
			return A[wiersz][wk[0]];
		else {
			suma = 0;
			m = 1;
			for (i = 0; i < stopien; i++) {
				k = 0;
				for (j = 0; j < stopien - 1; j++) {
					if (k == i)
						k++;
					kolumny[j] = wk[k++];
				}
				double part = m * calculateDeterminant(stopien - 1, wiersz + 1, kolumny, A);
				if (stopien == width) {
					tempMatrix[wiersz][wk[i]] = part;
				}
				suma += A[wiersz][wk[i]] * part;
				m = -m;
			}
			return suma;
		}
	}

	public boolean isInvertable() {
		double det = 0.0;
		try {
			det = getDeterminant();
		} catch (MatrixException e) {
			return false;
		}
		return det != 0.0;
	}

	private double getTempDeterminant(int ommitedX, int ommitedY)
			throws MatrixException {
		int xDiff = 0;
		int yDiff = 0;

		for (int x = 0; x < width - 1; x++) {
			if (x == ommitedX) {
				xDiff = 1;
			}
			yDiff = 0;
			for (int y = 0; y < height - 1; y++) {
				if (y == ommitedY) {
					yDiff = 1;
				}
				tempMatrix[y][x] = matrix[y + yDiff][x + xDiff];
			}
		}

		return getDeterminant(tempMatrix, width - 1, height - 1);
	}

	public Matrix getInverseMatrix() throws MatrixException {
		if (inverse != null){
			return inverse;
		}
		if (width != height) {
			throw new MatrixException(null,
					"matrix not square, no inverse matrix");
		}
		double det = getDeterminant();
		if (det == 0) {
			throw new MatrixException(null,
					"determinant is zero, no inverse matrix");
		}
		inverse = new Matrix(width, height);
		for (int x = 0; x < width; x++) {
			inverse.setElement(x, 0, tempMatrix[0][x]);
		}
		int firstInRowMultiplyer = -1;
		for (int x = 0; x < width; x++) {
			int multiplyer = firstInRowMultiplyer;
			for (int y = 1; y < height; y++) {
				inverse.setElement(x, y, multiplyer * getTempDeterminant(x, y));
				multiplyer = -multiplyer;
			}
			firstInRowMultiplyer = -firstInRowMultiplyer;
		}
		System.out.println("dopelnienia");
		inverse.printMatrix();
		inverse.transponujThis();
		System.out.println("transponowane");
		inverse.printMatrix();
		inverse.multiplyThisBy(1.0 / det);
		return inverse;
	}

	public void multiplyThisBy(double value) {
		for (int x = 0; x < width; x++) {
			for (int y = 0; y < height; y++) {
				matrix[y][x] *= value;
			}
		}
	}

	public Matrix multiplyBy(double value) {
		Matrix copy = new Matrix(width, height);

		for (int x = 0; x < width; x++) {
			for (int y = 0; y < height; y++) {
				copy.setElement(x, y, matrix[x][y] * value);
			}
		}
		return copy;
	}

	public Matrix multiplyBy(Matrix otherMatrix) throws MatrixException {
		if (width != otherMatrix.height) {
			throw new MatrixException(null,
					"wrong matrix row count, cannot multiply");
		}
		Matrix returned = new Matrix(otherMatrix.width, height);
		for (int x = 0; x < otherMatrix.width; x++) {
			for (int y = 0; y < height; y++) {
				double sum = 0.0;
				for (int w = 0; w < width; w++) {
					sum += matrix[y][w] * otherMatrix.matrix[w][x];
				}
				returned.setElement(x, y, sum);
			}
		}
		return returned;
	}

	public void printMatrix() {
//		System.out.println("width = " + width + ", height = " + height);
//		for (int y = 0; y < height; y++) {
//			for (int x = 0; x < width; x++) {
//				System.out.print(matrix[y][x] + "\t");
//			}
//			System.out.println("");
//		}
//		System.out.println("");
		printMatrix(matrix);
	}
	
	private void printMatrix(double[][] m){
		System.out.println("width = " + m[0].length + ", height = " + m.length);
		for (int y = 0; y < m.length; y++) {
			for (int x = 0; x < m[0].length; x++) {
				System.out.print(m[y][x] + "\t");
			}
			System.out.println("");
		}
		System.out.println("");
	}
	
	public void transponujThis(){
		for (int x=0; x<width; x++){
			for (int y=0; y<=x; y++){
				double temp = matrix[y][x];
				matrix[y][x] = matrix[x][y];
				matrix[x][y] = temp;
			}
		}
	}
	
	public Matrix transponuj(){
		Matrix other = new Matrix (this.width, this.height);
		for (int x=0; x<width; x++){
			for (int y=0; y<=x; y++){
				other.matrix[y][x] = matrix[x][y];
				other.matrix[x][y] = matrix[y][x];
			}
		}
		return other;
	}
}
